package com.cxl.juc;

import jdk.internal.misc.Unsafe;

import java.lang.reflect.Field;
import java.util.concurrent.locks.LockSupport;

/**
 * @Author: cxl
 * @CreateTime: 2021-04-08
 * @Description:
 */

public class MinReentrantLock implements MyLock {
    // 手写ReentrantLock
    /***
     * 0 未加锁
     * > 0  加锁
     */
    private volatile int state;

    /**
     * 独占锁模式
     * 同一时刻只有一个线程可以获取到锁 其他的线程会被阻塞
     */
    private Thread exclusiveThread;

    /**
     * 需要两个引用维护 阻塞队列
     * 1. head 指向队列的头
     * 2. Tail 指向队列的尾
     */
    // head 对应到当前占用锁的线程
    private Node head;

    private Node tail;


    /***
     * 阻塞的队列放在FIFO队列 先入先出
     * 封装一个node
     */
    static final class Node {
        // 前置引用
        Node prev;
        // 后置引用
        Node next;
        // 线程本身
        Thread thread;

        public Node() {
        }

        public Node(Thread thread) {
            this.thread = thread;
        }

        public Node(Node prev, Node next, Thread thread) {
            this.prev = prev;
            this.next = next;
            this.thread = thread;
        }
    }

    /***
     * 加锁假设公平锁 先到先拿锁
     * lock 过程
     * 1. 线程进入 发现state == 0 直接抢锁
     * 2. 进入发现 state > 0 放进队列
     */
    public void lock() {
        acquire(1);
    }

    /***
     * 尝试获取锁
     * 1. 获取成功 返回
     * 2. 抢占失败 阻塞当前线程
     * @param arg
     */
    private void acquire(int arg) {

        if (!tryAcquire(arg)) {
            // 更复杂逻辑
            Node node = addWirter();
            acquireQueued(node, arg);
        }
    }

    /***
     * 尝试抢占锁失败之后？
     * 1. 需要把当前线程封装成node 加入到阻塞队列
     * 2. 需要把当前线程park调 使线程挂起
     *
     * 唤醒后呢？
     * 1. 检查当前node节点 是否为 head.next（head.next 才可以拥有抢占锁的权限）
     * 2. 抢占
     *    成功：1、当前node设置为head, 老的head出队，返回业务层
     *    失败：2. 继续park 等待被唤醒
     *
     * ---》
     * 1. 添加到阻塞队列的逻辑 addWaiter();
     * 2. 竞争资源 acquireQueued
     */

    /***
     * 当前线程入队
     * @return node
     */
    private Node addWirter() {
        Node newNode = new Node(Thread.currentThread());

        // 如何入队  前置条件 已经有等待队列
        // 1. 找到newnode的pred
        // 2. 更新newnode.prev=pred
        // 3. cas 更新 tail = newNode
        // 4. 更新 pred.next  = newNode
        Node pred = tail;
        if (pred != null) {
            newNode.prev = pred;
            // 入队成功
            if (compareAndSetTail(pred, newNode)) {
                pred.next = newNode;
                return newNode;
            }
        }

        // 执行完毕之后 保证入队成功
        enq(newNode);
        return newNode;
    }

    private void acquireQueued(Node node, int arg) {
        for (; ; ) {
            // head 是持锁线程
            Node pred = node.prev;
            // 当前线程有权限抢占锁
            if (pred == head && tryAcquire(arg)) {
                // 说明竞争成功
                // 设置当前head为当前线程的node
                // 协助原始head 出队
                setHead(node);
                pred.next = null; // help gc
                return;
            }
            System.out.println("线程：" + Thread.currentThread().getName() + "挂起");
            // park
            LockSupport.park();

            System.out.println("线程：" + Thread.currentThread().getName() + "唤醒");
        }
    }

    /**
     * 1. 队列为空
     * 2. 入队失败
     * <p>
     * 自旋入队 成功才返回
     *
     * @param node
     */
    private void enq(Node node) {
        for (; ; ) {
            // 空队列
            // 当前线程是第一个抢锁失败的线程  当前持锁线程 没有设置任何node ，所以做为第一个线程的后边线程 给当前持锁线程补充node
            // head 在任何时候 都是当前占锁的线程
            if (tail == null) {
                // head 只会有一个线程设置成功
                if (compareAndSetHead(new Node())) {
                    tail = head;
                }
            } else {
                // 说明有等待的队列了 追加node
                Node pred = tail;
                if (pred != null) {
                    node.prev = pred;
                    // 入队成功
                    if (compareAndSetTail(pred, node)) {
                        pred.next = node;
                        return;
                    }
                }
            }

        }
    }


    /**
     * 尝试加锁
     *
     * @param arg
     * @return
     */
    private boolean tryAcquire(int arg) {

        if (state == 0) {
            // 不可以直接抢锁 因为模拟公平锁
            if (!hasQueuePredecessor() && compareAndSetState(0, arg)) {
                //  获取到锁
                this.exclusiveThread = Thread.currentThread();
                return true;
            }

            // 当前锁被占用 执行else if
            // 条件成立 说明当前线程是迟锁线程 并且更新state
        } else if (Thread.currentThread() == this.exclusiveThread) {
            int c = getState();
            // 这里不存在并发 所以不需要cas
            // 此处说明了重入锁
            c = c + arg;

            // 需要越界判断..
            this.state = c;
            return true;
        }

        // 1.cas加锁失败
        // 2. state > 0 && 当前线程不是迟锁线程
        return false;
    }

    /**
     * true 表示 当前线程之前有等待的线程
     * false 没有
     * 调用链
     * lock -> acquire -> tryAcquire -> hasQueuePredecessor
     * <p>
     * 什么时候是false
     * 1. 当前队列是空
     * 2. 当前head.next 无权限争锁
     *
     * @return
     */
    private boolean hasQueuePredecessor() {
        Node h = head;
        Node t = tail;
        Node s;

        // 条件一 h!=t 说明当前队列有node

        return h != t && ((s = h.next) == null || s.thread != Thread.currentThread());
    }


    public void unlock() {
        release(1);
    }

    private void release(int arg) {
        // 释放锁完毕
        // 阻塞队列如果还有线程 则唤醒一个
        if (tryRelease(arg)) {
            Node head = this.head;
            if (head.next != null) {
                // 唤醒head.next
                unparkSuccessor(head);
            }
        }
    }

    private void unparkSuccessor(Node node) {
        Node s = node.next;
        if (s != null && s.thread != null) {
            LockSupport.unpark(s.thread);
        }
    }

    /***
     * 完全释放锁
     * @param arg
     * @return
     */
    private boolean tryRelease(int arg) {
        int c = getState() - arg;
        if (getExclusiveThread() != Thread.currentThread()) {
            throw new RuntimeException("this Thead is not lock Thread");
        }

        // 执行到这 不存在并发
        if (c == 0) {
            this.exclusiveThread = null;
            this.state = c;
            return true;
        }
        // 锁没释放完
        this.state = c;
        return false;
    }

    /**
     * 设置head为当前node
     *
     * @param node
     */
    private void setHead(Node node) {
        this.head = node;
        // 当前node已经是获取锁成功的线程了
        node.thread = null;
        //  断开之前的引用
        node.prev = null;
    }

    public int getState() {
        return state;
    }

    public void setState(int state) {
        this.state = state;
    }

    public Node getHead() {
        return head;
    }

    public Node getTail() {
        return tail;
    }

    public void setTail(Node tail) {
        this.tail = tail;
    }

    public Thread getExclusiveThread() {
        return exclusiveThread;
    }

    public void setExclusiveThread(Thread exclusiveThread) {
        this.exclusiveThread = exclusiveThread;
    }

    /**
     * 通过cas设置head 和 tail
     * 通过unsafe获取数据值
     */
    private static final Unsafe unsafe;
    private static final long stateOffset;
    private static final long headOffset;
    private static final long tailOffset;

    static {
        try {
            Field f = Unsafe.class.getDeclaredField("theUnsafe");
            f.setAccessible(true);
            unsafe = (Unsafe) f.get(null);
            stateOffset = unsafe.objectFieldOffset(MinReentrantLock.class.getDeclaredField("state"));
            headOffset = unsafe.objectFieldOffset(MinReentrantLock.class.getDeclaredField("head"));
            tailOffset = unsafe.objectFieldOffset(MinReentrantLock.class.getDeclaredField("tail"));


        } catch (Exception e) {
            throw new Error(e);
        }
    }

    private final boolean compareAndSetHead(Node update) {
        return unsafe.compareAndSetObject(this, headOffset, null, update);
    }

    private final boolean compareAndSetTail(Node expect, Node update) {
        return unsafe.compareAndSetObject(this, tailOffset, expect, update);
    }

    private final boolean compareAndSetState(int expect, int update) {
        return unsafe.compareAndSetObject(this, stateOffset, expect, update);
    }


}
